package com.tanhua.dubbo.es.api;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.dubbo.config.annotation.Service;
import com.tanhua.dubbo.server.api.UserLocationApi;
import com.tanhua.dubbo.server.pojo.UserLocation;
import com.tanhua.dubbo.server.vo.PageInfo;
import com.tanhua.dubbo.server.vo.UserLocationVo;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.sort.GeoDistanceSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.*;

import java.util.HashMap;
import java.util.Map;

@Service(version = "1.0.0")
@Slf4j
public class UserLocationApiImpl implements UserLocationApi {

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    /**
     * 初始化索引库
     */
    public void initIndex() {
        //判断索引库是否存在，如果不存在，需要创建
        if (!elasticsearchTemplate.indexExists("tanhua")) {
            elasticsearchTemplate.createIndex(UserLocation.class);
        }
        //判断表是否存在，如果不存在，需要创建
        if (!elasticsearchTemplate.typeExists("tanhua", "user_location")) {
            elasticsearchTemplate.putMapping(UserLocation.class);
        }
    }

    /**
     * 更新用户地理位置
     *
     * @param userId    用户id
     * @param longitude 经度
     * @param latitude  纬度
     * @param address   地址名称
     * @return
     */
    @Override
    public Boolean updateUserLocation(Long userId, Double longitude, Double latitude, String address) {
        try {
            //查询个人的地理位置数据，如果不存在，需要新增，如果是存在数据，更新数据
            GetQuery query = new GetQuery();
            query.setId(String.valueOf(userId));
            UserLocation userLocation = elasticsearchTemplate.queryForObject(query, UserLocation.class);

            if (ObjectUtil.isEmpty(userLocation)) {
                //新增数据
                userLocation = new UserLocation();
                userLocation.setUserId(userId);
                userLocation.setAddress(address);
                userLocation.setCreated(System.currentTimeMillis());
                userLocation.setUpdated(userLocation.getCreated());
                userLocation.setLastUpdated(userLocation.getCreated());
                userLocation.setLocation(new GeoPoint(latitude, longitude));

                IndexQuery queryIndex = new IndexQueryBuilder().withObject(userLocation).build();
                //保存数据到es中
                elasticsearchTemplate.index(queryIndex);
            } else {
                //更新数据
                //更新的字段
                Map<String, Object> map = new HashMap<>();
                map.put("location", new GeoPoint(latitude, longitude));
                map.put("updated", System.currentTimeMillis());
                map.put("lastUpdated", userLocation.getUpdated());
                map.put("address", address);


                UpdateRequest updateRequet = new UpdateRequest();
                updateRequet.doc(map);

                UpdateQuery updateQuery = new UpdateQueryBuilder().
                        withId(String.valueOf(userId)).
                        withClass(UserLocation.class).
                        withUpdateRequest(updateRequet).build();
                //更新数据
                elasticsearchTemplate.update(updateQuery);
            }
            return true;
        } catch (Exception e) {
            log.error("更新地理位置失败~ userId = " + userId + ", longitude = " + longitude + ", latitude = " + latitude + ", address = " + address, e);
        }
        return false;
    }

    /**
     * 根据用户ID查询位置信息
     *
     * @param userId 用户ID
     * @return
     */
    @Override
    public UserLocationVo findLocationById(Long userId) {
        GetQuery query = new GetQuery();
        query.setId(StrUtil.toString(userId));
        //es模板用于查询的方法
        UserLocation userLocation = elasticsearchTemplate.queryForObject(query, UserLocation.class);
        if (ObjectUtil.isNotEmpty(userLocation)) {
            //userLocation对象中包含GeoPoint对象无法被序列化,需要使用UserLocationVo中自定义的format方法对经纬度进行解析
            return UserLocationVo.format(userLocation);
        }
        return null;
    }

    /**
     * 根据位置查询用户
     *
     * @param longitude 经度
     * @param latitude  纬度
     * @param distance  距离
     * @param page      当前页
     * @param pageSize  页数
     * @return
     */
    @Override
    public PageInfo<UserLocationVo> findUserByLocation(Double longitude, Double latitude, Double distance, Integer page, Integer pageSize) {
        PageInfo<UserLocationVo> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);
        //实现了SearchQuery接口，支持构造分页、排序
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        //分页
        Pageable pageRequest = PageRequest.of(page - 1, pageSize);
        searchQueryBuilder.withPageable(pageRequest);

        BoolQueryBuilder queryBuilder = new BoolQueryBuilder();

        //以一个点为中心，指定范围查询
        GeoDistanceQueryBuilder distanceQueryBuilder = new GeoDistanceQueryBuilder("location");
        //中心点
        distanceQueryBuilder.point(latitude, longitude);
        //距离（画圆的半径）单位：公里
        distanceQueryBuilder.distance(distance / 1000, DistanceUnit.KILOMETERS);

        queryBuilder.must(distanceQueryBuilder);
        searchQueryBuilder.withQuery(queryBuilder);

        //排序，由近到远排序
        GeoDistanceSortBuilder geoDistanceSortBuilder = new GeoDistanceSortBuilder("location", latitude, longitude);
        geoDistanceSortBuilder.order(SortOrder.ASC);//正序排序
        geoDistanceSortBuilder.unit(DistanceUnit.KILOMETERS);//设置单位:公里
        searchQueryBuilder.withSort(geoDistanceSortBuilder);

        AggregatedPage<UserLocation> userLocations =
                elasticsearchTemplate.queryForPage(searchQueryBuilder.build(), UserLocation.class);

        if (ObjectUtil.isEmpty(userLocations.getContent())) {
            return pageInfo;
        }
        pageInfo.setRecords(UserLocationVo.formatToList(userLocations.getContent()));
        return pageInfo;
    }
}
